//
// Created by 吴竹亚 on 2023/4/20.
//

#include "tcp.h"

int dataSize = 0;

void initNode(NODE **p) {
    *p = (NODE *) malloc(sizeof(NODE));
    (*p)->myTcpSocket = NULL;
    (*p)->prior = NULL;
    (*p)->next = NULL;
}

NODE *addNode(NODE *head, MyTcpSocket *data) {
    NODE *p;
    initNode(&p);
    p->myTcpSocket = data;
    NODE *temp = head;
    while (temp->next) {
        temp = temp->next;
    }
    temp->next = p;
    p->prior = temp;
    return p;
}

NODE *head = NULL;


void tcp_server(int port) {
    pthread_t pthread;
    initNode(&head);
    int serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSocket < 0) {
        printf("socket error!");
        exit(-1);
    }
    struct sockaddr_in serverAddr;
    serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    serverAddr.sin_port = htons(port);
    serverAddr.sin_family = AF_INET;
    socklen_t addrSize = sizeof(struct sockaddr);
    int bindStatus = bind(serverSocket, (struct sockaddr *) &serverAddr, addrSize);
    if (bindStatus < 0) {
        printf("bind error!");
        exit(-1);
    }
    int listenStatus = listen(serverSocket, MAX_CLIENT_SIZE);
    if (listenStatus < 0) {
        printf("listen error!");
        exit(-1);
    }
    pthread_create(&pthread, NULL, tcp_accept, (void *) &serverSocket);
    char message[1024];
    int clientFd;
    while (1) {
        memset(message, 0, 1024);
        memset(&clientFd, 0, sizeof(int));
        printf("client socket fd: ");
        scanf("%d", &clientFd);
        printf("input message: ");
        scanf("%s", message);
        ssize_t i = send(clientFd, message, strlen(message), 0);
        if (!i) {
            printf("send %d error\n", clientFd);
        }
    }
}

void *tcp_accept(void *serverSocket) {
    int socketServer = (*(int *) serverSocket);
    pthread_t pthread;
    while (1) {
        if (dataSize < MAX_CLIENT_SIZE) {
            socklen_t i = sizeof(struct sockaddr);
            struct sockaddr_in cliAddr;
            int clientSocket = accept(socketServer, (struct sockaddr *) &cliAddr, &i);
            if (!clientSocket) {
                printf("client error!\n");
                break;
            }
            printf("socket client %d connection\n", clientSocket);
            MyTcpSocket *myTcpSocket = (MyTcpSocket *) malloc(sizeof(myTcpSocket));
            myTcpSocket->socket = clientSocket;
            myTcpSocket->cli_addr = cliAddr;
            NODE *node = addNode(head, myTcpSocket);
            dataSize++;
            pthread_create(&pthread, NULL, tcp_recv, (void *) node);
        } else {
            printf("connect exceeding maximum quantity\n");
            sleep(1);
        }
    }
}

void *tcp_recv(void *node) {
    NODE *p = (NODE *) node;
    struct sockaddr_in addr = p->myTcpSocket->cli_addr;
    char buf[1024];
    int i = recv(p->myTcpSocket->socket, buf, 1024, 0);
    while (i > 0) {
        printf("host: %s,port: %d ---> message: %s\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
        i = recv(p->myTcpSocket->socket, buf, 1024, 0);
    }
    printf("socket client %d connection error\n", p->myTcpSocket->socket);
    close(p->myTcpSocket->socket);
    p->prior->next = p->next;
    if (p->next) {
        p->next->prior = p->prior;
    }
    dataSize--;
    free(p);
}

void tcp_client() {
    pthread_t tcpClientRecv;
    char message[1024];
    char host[1024];
    int serverPort;
    memset(host, 0, 1024);
    memset(&serverPort, 0, 4);
    printf("input address: ");
    scanf("%s", host);
    printf("input port: ");
    scanf("%d", &serverPort);
    int sockDes = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockDes < 0) {
        printf("socket error!\n");
        return;
    }
    struct sockaddr_in addr;
    addr.sin_addr.s_addr = inet_addr(host);
    addr.sin_port = htons(serverPort);
    addr.sin_family = AF_INET;
    int connectStatus = connect(sockDes, (struct sockaddr *) &addr, sizeof(addr));
    if (connectStatus < 0) {
        printf("connection error!");
        return;
    }
    MyTcpSocket myTcpSocket = {sockDes, addr};
    // 开启新线程接收服务端消息
    pthread_create(&tcpClientRecv, NULL, tcp_client_recv_message, (void *) &myTcpSocket);
    while (1) {
        memset(message, 0, 1024);
        printf("input message: ");
        scanf("%s", message);
        ssize_t i = send(sockDes, message, 1024, 0);
        if (i < 0) {
            printf("send error!");
            exit(-1);
        }
    }
}

void *tcp_client_recv_message(void *mySocket) {
    MyTcpSocket socket = (*(MyTcpSocket *) mySocket);
    int socketClient = socket.socket;
    struct sockaddr_in cli_addr = socket.cli_addr;
    char buf[1024];
    int i = recv(socketClient, buf, 1024, 0);
    while (i > 0) {
        printf("%s:%d -> %s\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);
        i = recv(socketClient, buf, 1024, 0);
    }
    close(socketClient);
    return NULL;
}

void tcp_server_select(int port) {
    pthread_t myThread;
    int socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socketServer < 0) {
        printf("socket error!");
        exit(-1);
    }
    struct sockaddr_in addr;
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    addr.sin_port = htons(port);
    addr.sin_family = AF_INET;
    int bindStatus = bind(socketServer, (struct sockaddr *) &addr, sizeof(struct sockaddr));
    if (bindStatus < 0) {
        printf("bind error!");
        exit(-2);
    }
    int listenStatus = listen(socketServer, 20);
    if (listenStatus < 0) {
        printf("listen error!");
        exit(-3);
    }
    pthread_create(&myThread, NULL, tcp_server_select_recv, (void *) &socketServer);
    int targetSocketFd;
    char targetMessage[1024];
    while (1) {
        memset(&targetSocketFd, 0, sizeof(int));
        memset(targetMessage, 0, 1024);
        printf("input clientFd: ");
        scanf("%d", &targetSocketFd);
        printf("input message: ");
        scanf("%s", targetMessage);
        send(targetSocketFd, targetMessage, strlen(targetMessage), 0);
    }
}

void *tcp_server_select_recv(void *socketServer) {
    int socketFd = (*(int *) socketServer);
    fd_set myFd, readFd;
    struct sockaddr_in addr;
    size_t addrLen = sizeof(addr);
    FD_ZERO(&myFd);
    FD_SET(socketFd, &myFd);
    char buff[1024];
    while (1) {
        readFd = myFd;
        memset(&addr, 0, sizeof(struct sockaddr_in));
        int selectStatus = select(FD_SETSIZE, &readFd, NULL, NULL, NULL);
        if (selectStatus < 0) {
            printf("select error!");
            close(socketFd);
            exit(-4);
        }

        for (int i = 0; i < FD_SETSIZE; ++i) {
            if (FD_ISSET(i, &readFd)) {
                if (i == socketFd) {
                    int socketClient = accept(socketFd, (struct sockaddr *) &addr, &addrLen);
                    if (socketClient < 0) {
                        printf("accept error!");
                        exit(-5);
                    }
                    printf("new client connection host:%s port:%d clientFd:%d\n", inet_ntoa(addr.sin_addr),
                           ntohs(addr.sin_port), socketClient);
                    FD_SET(socketClient, &myFd);
                } else {
                    memset(buff, 0, 1024);
                    ssize_t recvStatus = recv(i, buff, 1024, 0);
                    getpeername(i, (struct sockaddr *) &addr, &addrLen);
                    if (recvStatus <= 0) {
                        printf("client disconnect host:%s port:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
                        close(i);
                        FD_CLR(i, &myFd);
                    } else {
                        printf("%s:%d --> %s\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buff);
                    }
                }
            }
        }
    }
}

